﻿namespace Raven
{
    public class ShotGun : RavenWeapon
    {
        //how much shot the each shell contains
        private int _numBallsInShell;

        //how much the shot spreads out when a cartridge is discharged
        private Fix _spread;

        public ShotGun(RavenBot owner)
            : base(WeaponType.type_shotgun, Constants.ShotGun_DefaultRounds, Constants.ShotGun_MaxRoundsCarried,
                Constants.ShotGun_FiringFreq, Constants.ShotGun_IdealRange, Constants.Pellet_MaxSpeed, owner)
        {
            _numBallsInShell = Constants.ShotGun_NumBallsInShell;
            _spread = Constants.ShotGun_Spread;
            //setup the fuzzy module
            InitializeFuzzyModule();
        }

        protected override void InitializeFuzzyModule()
        {
            _fuzzyModule = new FuzzyModule();
            FuzzyVariable DistanceToTarget = _fuzzyModule.CreateFLV("DistanceToTarget");

            FzSet Target_Close = DistanceToTarget.AddLeftShoulderSet("Target_Close", 0, 25, 150);
            FzSet Target_Medium = DistanceToTarget.AddTriangularSet("Target_Medium", 25, 150, 300);
            FzSet Target_Far = DistanceToTarget.AddRightShoulderSet("Target_Far", 150, 300, 1000);

            FuzzyVariable Desirability = _fuzzyModule.CreateFLV("Desirability");

            FzSet VeryDesirable = Desirability.AddRightShoulderSet("VeryDesirable", 50, 75, 100);
            FzSet Desirable = Desirability.AddTriangularSet("Desirable", 25, 50, 75);
            FzSet Undesirable = Desirability.AddLeftShoulderSet("Undesirable", 0, 25, 50);

            FuzzyVariable AmmoStatus = _fuzzyModule.CreateFLV("AmmoStatus");
            FzSet Ammo_Loads = AmmoStatus.AddRightShoulderSet("Ammo_Loads", 30, 60, 100);
            FzSet Ammo_Okay = AmmoStatus.AddTriangularSet("Ammo_Okay", 0, 30, 60);
            FzSet Ammo_Low = AmmoStatus.AddTriangularSet("Ammo_Low", 0, 0, 30);


            _fuzzyModule.AddRule(new FzAND(Target_Close, Ammo_Loads), VeryDesirable);
            _fuzzyModule.AddRule(new FzAND(Target_Close, Ammo_Okay), VeryDesirable);
            _fuzzyModule.AddRule(new FzAND(Target_Close, Ammo_Low), VeryDesirable);

            _fuzzyModule.AddRule(new FzAND(Target_Medium, Ammo_Loads), VeryDesirable);
            _fuzzyModule.AddRule(new FzAND(Target_Medium, Ammo_Okay), Desirable);
            _fuzzyModule.AddRule(new FzAND(Target_Medium, Ammo_Low), Undesirable);

            _fuzzyModule.AddRule(new FzAND(Target_Far, Ammo_Loads), Desirable);
            _fuzzyModule.AddRule(new FzAND(Target_Far, Ammo_Okay), Undesirable);
            _fuzzyModule.AddRule(new FzAND(Target_Far, Ammo_Low), Undesirable);
        }

        public override void Destroy()
        {
        }

        public override void ShootAt(Vector2 pos)
        {
            if (NumRoundsRemaining() > 0 && isReadyForNextShot() && _owner.hasLOSto(pos))
            {
                //a shotgun cartridge contains lots of tiny metal balls called pellets. 
                //Therefore, every time the shotgun is discharged we have to calculate
                //the spread of the pellets and add one for each trajectory
                for (int b = 0; b < _numBallsInShell; ++b)
                {
                    //determine deviation from target using a bell curve type distribution
                    Fix deviation = _owner.GetWorld().GetRandom().RandInRange(0, _spread) +
                        _owner.GetWorld().GetRandom().RandInRange(0, _spread) - _spread;

                    Vector2 AdjustedTarget = pos - _owner.Pos();

                    //rotate the target vector by the deviation
                    Transformations.Vec2DRotateAroundOrigin(ref AdjustedTarget, deviation);

                    //add a pellet to the game world
                    _owner.GetWorld().AddShotGunPellet(_owner, AdjustedTarget + _owner.Pos());
                }

                _numRoundsLeft--;

                UpdateTimeWeaponIsNextAvailable();

                //add a trigger to the game so that the other bots can hear this shot
                //(provided they are within range)
                _owner.GetWorld().GetMap().AddSoundTrigger(_owner, Constants.ShotGun_SoundRange);
            }
        }

        public override Fix GetDesirability(Fix DistToTarget)
        {
            if (_numRoundsLeft == 0)
            {
                _lastDesirabilityScore = 0;
            }
            else
            {
                //fuzzify distance and amount of ammo
                _fuzzyModule.Fuzzify("DistanceToTarget", DistToTarget);
                _fuzzyModule.Fuzzify("AmmoStatus", _numRoundsLeft);

                _lastDesirabilityScore = _fuzzyModule.DeFuzzify("Desirability");
            }

            return _lastDesirabilityScore;
        }
    }
}